package com.eagle.epay.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eagle.epay.entity.Product;
import com.eagle.epay.entity.RedisCart;
import com.eagle.epay.enums.ProductStatusEnum;
import com.eagle.epay.exception.ServiceException;
import com.eagle.epay.form.CartAddForm;
import com.eagle.epay.form.CartUpdateForm;
import com.eagle.epay.service.IProductService;
import com.eagle.epay.service.IShoppingCartService;
import com.eagle.epay.vo.CartProductVo;
import com.eagle.epay.vo.CartVo;
import com.eagle.epay.vo.ProductVo;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Service
public class ShoppingCartService implements IShoppingCartService {
	@Autowired
	IProductService productService;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Override
	public CartVo addToCart(String uid, CartAddForm cartAddForm) {
		/**
		 * 1 商品是否存在
		 * 2 商品是否正常在售
		 * 3 商品库存是否充足
		 */
		QueryWrapper<Product> wrapper = new QueryWrapper();
		wrapper.eq("id",cartAddForm.getProductId());
		Product product= this.productService.getOne(wrapper);
		if(null == product){
			throw new ServiceException("The product does not exist");
		}
		if(product.getStatus().equals(ProductStatusEnum.OFF_SALE)||
				product.getStatus().equals(ProductStatusEnum.DELETE)){
			throw new ServiceException("The product already off sale");
		}

		if(product.getStock()<=0){
			throw new ServiceException("The product already sell");
		}

		//add to redis
		RedisCart redisCart = new RedisCart();
		redisCart.setProductId(cartAddForm.getProductId());
		redisCart.setQuantity(cartAddForm.getQuantity());
		redisCart.setProductSelected(cartAddForm.getSelected());
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		Gson gson = new Gson();
		String existingJsonData = opt.get(uid, String.valueOf(redisCart.getProductId()));

		if(StringUtils.isNotEmpty(existingJsonData)){
			RedisCart existingCart = gson.fromJson(existingJsonData, RedisCart.class);
			int updatedQuantity = existingCart.getQuantity() + redisCart.getQuantity();
			existingCart.setQuantity(updatedQuantity);
			redisCart = existingCart;
		}

		String jsonData = gson.toJson(redisCart);
		opt.put(uid,String.valueOf(redisCart.getProductId()),jsonData);
		return this.list(uid);
	}

	@Override
	public CartVo updateCart(String uid, CartUpdateForm carUpdateForm) {
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		String jsonDate = opt.get(uid, String.valueOf(carUpdateForm.getProductId()));
		if(StringUtils.isEmpty(jsonDate)){
			throw new ServiceException("the product not in cart");
		}
		if(null == carUpdateForm.getQuantity() && null == carUpdateForm.getSelected()){
			throw new ServiceException("no element to update in cart");
		}
		Gson gson = new Gson();
		RedisCart redisCart = gson.fromJson(jsonDate, RedisCart.class);
		if(null != carUpdateForm.getQuantity()){
			redisCart.setQuantity(redisCart.getQuantity()+carUpdateForm.getQuantity());
		}
		if(null != carUpdateForm.getSelected()){
			redisCart.setProductSelected(carUpdateForm.getSelected());
		}

		opt.put(uid,String.valueOf(redisCart.getProductId()),gson.toJson(redisCart));
		return this.list(uid);
	}

	@Override
	public CartVo deleteCart(String uid, CartUpdateForm carUpdateForm) {
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		String jsonDate = opt.get(uid, String.valueOf(carUpdateForm.getProductId()));
		if(StringUtils.isEmpty(jsonDate)){
			throw new ServiceException("the product not in cart");
		}
		opt.delete(uid,String.valueOf(carUpdateForm.getProductId()));
		return this.list(uid);
	}

	private void changeSelectedStatus(String uid, boolean selectedStatus){
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		Gson gson = new Gson();
		opt.entries(uid).forEach((k,v) ->{
			RedisCart redisCart = gson.fromJson(v, RedisCart.class);
			redisCart.setProductSelected(selectedStatus);
			opt.put(uid,k,gson.toJson(redisCart));
		});
	}
	@Override
	public CartVo selectAll(String uid) {
		this.changeSelectedStatus(uid, true);
		return this.list(uid);
	}

	@Override
	public CartVo unSelectAll(String uid) {
		this.changeSelectedStatus(uid, false);
		return this.list(uid);
	}

	@Override
	public Integer totalQuantity(String uid) {
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		List<RedisCart> redisCartList = new ArrayList();
		Gson gson = new Gson();
		opt.entries(uid).forEach((k,v) ->{
			RedisCart redisCart = gson.fromJson(v, RedisCart.class);
			redisCartList.add(redisCart);
		});
		Integer reduce = redisCartList.stream().map(RedisCart::getQuantity).reduce(0, Integer::sum);
		return reduce;
	}

	public CartVo list(String uid){
		HashOperations<String, String, String> opt = redisTemplate.opsForHash();
		Gson gson = new Gson();
		CartVo carVo = new CartVo();
		List<CartProductVo> redisCartProductVos = new ArrayList();

		Set<String> productIds = new HashSet();
		opt.entries(uid).forEach((k,v) ->{
			productIds.add(k);
		});
		QueryWrapper<Product> wrapper = new QueryWrapper();
		wrapper.in("id",productIds);
		List<Product> productList = this.productService.list(wrapper);
		//批量查询product list
		ConcurrentHashMap<String, Product> productMap = productList.stream()
				.collect(Collectors.toMap(e -> String.valueOf(e.getId()), e -> e, (a, b) -> b, ConcurrentHashMap::new));

		opt.entries(uid).forEach((k,v) ->{
			String productId = k;
			Product product = productMap.get(productId);
			if(null != product){
				RedisCart redisCart = gson.fromJson(v, RedisCart.class);
				CartProductVo redisCartProduct = new CartProductVo(product.getId(),
						redisCart.getQuantity(),
						product.getName(),
						product.getSubtitle(),
						product.getMainImage(),
						product.getPrice(),
						product.getStatus(),
						product.getPrice().multiply(BigDecimal.valueOf(redisCart.getQuantity())),
						product.getStock(),
						redisCart.getProductSelected()
				);
				redisCartProductVos.add(redisCartProduct);
			}
		});

		List<CartProductVo> selectedRedisCartProductVos = redisCartProductVos.stream()
				.filter(s -> s.getProductSelected().equals(Boolean.TRUE))
				.collect(Collectors.toList());
		Integer cartTotalQuantity = 0;
		BigDecimal cartTotalPrice = BigDecimal.valueOf(0);
		for (CartProductVo cartProductVo : selectedRedisCartProductVos) {
			cartTotalQuantity += cartProductVo.getQuantity();
			cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductPrice()
					.multiply(BigDecimal.valueOf(cartProductVo.getQuantity())));
		}

		carVo.setCartProductVoList(redisCartProductVos);
		carVo.setSelectedAll(redisCartProductVos.size() ==  selectedRedisCartProductVos.size()? true : false);
		carVo.setCartTotalQuantity(cartTotalQuantity);
		carVo.setCartTotalPrice(cartTotalPrice);
		return carVo;
	}
}
